import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_demo/pages/page_with_title.dart';
import 'package:flutter_demo/widgets/app_widgets.dart';

class BlocPage extends PageWithTitle {
  const BlocPage() : super("状态管理（Bloc）");

  @override
  List<Widget> buildContentWidgets(BuildContext context) {
    return const [
      PrimaryTitle("简介"),
      BodyText("一般我们页面模块化设计的话，会将逻辑拆分到不同的组件中，而bloc就是实现跨组件的"
          "状态共享的框架"),
      LabelText("类似在Android中我们将列表页面与详情页面采用不同的Fragment实现，以此达到手"
          "机、平板设备上不同的页面展示方式（手机上通常是列表页跳转详情页，平板上通常是左侧列表"
          "右侧详情页，在Android中跨组件的状态共享一般采用ViewModel或者全局的LiveData实现）"),
      AppSpacer.large,
      _Example(),
      UlItem("BlocProvider MultiBlocProvider"),
      BodyText("状态的提供者，为子组件提供状态查询与订阅"),
      LabelText("- 通常状态封装在一个Cubit中，它是一个状态流，通过emit更新状态，通常我们会在"
          "类里面封装业务逻辑，在业务方法被调用后更新状态"),
      LabelText("- 子组件要获取Cubit，可以直接通过BlocProvider.of(context)获取到最近的Cubit"),
      LabelText("- 为了避免多层嵌套，Bloc提供了MultiBlocProvider来一次性注入多个Cubit"),
      AppSpacer.large,
      UlItem("BlocListener MultiBlocListener"),
      BodyText("状态的监听，构造一个监听器来监听Cubit状态变化，内部封装了组件生命周期变化的监听处理"),
      LabelText("- bloc Cubit对象，不指定的话会根据类型在上下文中自动查找最近的实例"),
      LabelText("- listenWhen 监听回调的过滤条件，仅当返回true时执行回调，默认不做任何过滤，"
          "开发者可以自行根据事件类型进行判断，避免不必要的更新"),
      LabelText("- child 嵌套的子组件，它并不会跟随监听回调自动重组，若有此需求请使用BlocBuilder"),
      LabelText("- listener 监听回调"),
      LabelText("- MultiBlocListener Bloc为多层监听嵌套提供的解决方案"),
      AppSpacer.large,
      UlItem("BlocBuilder"),
      BodyText("基于BlocListener封装的StatefulWidget，他会在收到状态更新时，重建widget，"
          "使用上与BlocListener大差不差"),
      LabelText(
          "- 该组件可以针对单一类型的Cubit进行状态订阅并更新，非常适合局部更新widget使用，"
              "若需要同时监听多个Cubit状态联合更新，建议嵌套使用，官方没有封装多监听类型"),
    ];
  }
}

///状态定义，状态为快照，不可修改里面的值以免发生状态不同步的问题
///更新状态需要创建新的快照，一般在子类实现，我们通过不同的子类类型来分辨不同事件的发生
///一般通用的状态值和方法会抽取到基类，而子类可以自己定义特有的状态值（例如网络请求错误的子类可以
///携带错误原因、网络提示，这样widget订阅到网络请求错误事件即可将提示显示到ui上）
@immutable
abstract class _CalcState {
  final int value1;

  final int value2;

  const _CalcState(this.value1, this.value2);

  _CalcState.of(_CalcState oldState, {int? value1, int? value2})
      : this(
          value1 ?? oldState.value1,
          value2 ?? oldState.value2,
        );

  int get sum => value1 + value2;
}

///所有状态都需要一个初始化状态
class _OnInit extends _CalcState {
  _OnInit() : super(0, 0);
}

///事件1 更新value1
class _OnValue1Update extends _CalcState {
  _OnValue1Update(super.oldState, int value1) : super.of(value1: value1);
}

///事件2 更新value2
class _OnValue2Update extends _CalcState {
  _OnValue2Update(super.oldState, int value2) : super.of(value2: value2);
}

class _CalcCubit extends Cubit<_CalcState> {
  _CalcCubit() : super(_OnInit());

  void addValue1() {
    emit(_OnValue1Update(state, state.value1 + 1));
  }

  void addValue2() {
    emit(_OnValue2Update(state, state.value2 + 1));
  }
}

class _Example extends StatelessWidget {
  const _Example();

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => _CalcCubit(),
      child: Column(
        children: [
          _SumWidget(),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              _AddValue1(),
              _AddValue2(),
            ],
          )
        ],
      ),
    );
  }
}

class _AddValue2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    _CalcCubit cubit = BlocProvider.of(context);
    return FilledButton(
      onPressed: () {
        cubit.addValue2();
      },
      child: BlocBuilder<_CalcCubit, _CalcState>(
        bloc: cubit,
        buildWhen: (previous, current) => current is _OnValue2Update,
        builder: (context, state) => Text("v2：${state.value2}"),
      ),
    );
  }
}

class _AddValue1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    _CalcCubit cubit = BlocProvider.of(context);
    return FilledButton(
      onPressed: () {
        cubit.addValue1();
      },
      child: BlocBuilder<_CalcCubit, _CalcState>(
        bloc: cubit,
        buildWhen: (previous, current) => current is _OnValue1Update,
        builder: (context, state) => Text("v1：${state.value1}"),
      ),
    );
  }
}

class _SumWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<_CalcCubit, _CalcState>(
        builder: (context, state) => Text("sum：${state.sum}"));
  }
}
